package com.gerald.currency;

/**
 * 在调用wait方法时，线程必须要持有被调用对象的锁，当调用wait方法后，线程就会释放掉该对象的锁（monitor），
 * 在调用sleep方法时，线程时不会释放对象的锁的
 *
 * 关于wait与notify和notifyAll方法的总结：
 * 1. 当调用wait时，首先应该确保调用了wait方法的 线程已经持有了对象的锁
 * 2. 当调用wait后，该线程就会释放掉这个对象的锁，然后进入到等待的状态（wait set）
 * 3. 当线程调用了wait后进入到等待的状态时，他就可以等待其他线程调用相同对象的notify或notifyAll方法来使得自己被唤醒
 * 4. 一旦这个线程被其他线程唤醒后，该线程就会与其他线程一同开始竞争这个对象的锁（公平竞争），只有当该线程获取到了这个对象的锁后，线程才会继续往下执行
 * 5. 调用wait方法的代码片段需要放在一个synchronized块或者是synchronized方法中，这样才可以确保线程在调用wait方法前已经获取到了对象的锁
 * 6. 当调用对象的notify方法时，它会随机唤醒该对象等待集合中的任意一个线程，当某个线程被唤醒后，它就会与其他线程一同竞争对象的锁
 * 7. 当调用对象的notifyAll方法时，它会唤醒该对象等待集合中的所有线程，这些线程被唤醒后，又会开始竞争对象的锁
 * 8. 在某一时刻，只有唯一一个线程可以拥有对象的锁
 */
public class MyTest {

    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();
        synchronized (object){
            object.wait();  
        }

    }

    /**
     * 面试题：一个线程正在执行method1时（还没执行完），这时候其他线程能不能执行method2()?
     *
     * 答案： 这是不可以的，因为对于类Test而言，其方法method1和method2其实用到的时同一个锁，这时不管method1或者时method2都是进不去的，因为method1已经被第一个线程执行，获取到了对象的锁
     *         但是，如果创建了两个对象，如下面，这时候就可以，因为有两个对象，不互相影响
     */
    class Test{
        public synchronized void method1(){}

        public synchronized void method2(){}
    }

    Test test1 = new Test();
    Test test2 = new Test();


    /**
     * 面试题衍生：换一种写法，一个线程正在执行method1时（还没执行完），这时候其他线程能不能执行method2()?
     *
     * 答案： 这个是可以的，因为method1它的锁是Test1的对象，而第二个方法method2它的锁是Test1对应的class对象，这是两个不同的对象，所以可以
     */
    static class Test1{
        public synchronized void method1(){}

        public static synchronized void method2(){}
    }

}
